package com.linser.gistools.utils;

import org.geotools.referencing.CRS;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Arrays;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

public class CapabilitiesCRSProvider {

    private BiFunction<String, String, String> codeMapper = CapabilitiesCRSProvider::mapCode;

    private final Set<String> authorityExclusions = new HashSet<>(Arrays.asList(
            "http://www.opengis.net/gml",
            "http://www.opengis.net/def",
            "AUTO", // only suitable in WMS service
            "AUTO2", // only suitable in WMS service
            "urn:ogc:def",
            "urn:x-ogc:def"
    ));

    private BiFunction<String, String, Boolean> codeFilter = CapabilitiesCRSProvider::filterCode;

    /**
     * 获取非权威的发布机构
     */
    public Set<String> getAuthorityExclusions() {
        return authorityExclusions;
    }

    public Set<String> getCodes() {
        // 获取所有支持的授权机构
        List<String> authorities =
                CRS.getSupportedAuthorities(true).stream()
                        .filter(a -> !authorityExclusions.contains(a))
                        .sorted()
                        .collect(Collectors.toList());

        return authorities.stream()
                .flatMap(
                        authority ->
                                CRS.getSupportedCodes(authority).stream()
                                        .filter(code -> codeFilter.apply(authority, code))
                                        .sorted((a, b) -> compareCodes(a, b))
                                        .map(code -> codeMapper.apply(authority, code)))
                .collect(Collectors.toCollection(() -> new LinkedHashSet<>()));
    }

    private int compareCodes(String a, String b) {
        try {
            return Integer.valueOf(a).compareTo(Integer.valueOf(b));
        } catch (NumberFormatException e) {
            return a.compareTo(b);
        }
    }

    /**
     * Sets a bi-function that the provider uses to go from authority and code to the CRS identifier
     * published in the capabilities document.
     *
     * @param codeMapper
     */
    public void setCodeMapper(BiFunction<String, String, String> codeMapper) {
        this.codeMapper = codeMapper;
    }

    /**
     * Sets a bi-function that the provider uses to filter codes from the capabilities document. The
     * function receives authority and code, and will return true if the code should be included in
     * the output. By default, the provider will filter out the WGS84(DD) code.
     *
     * @param codeFilter
     */
    public void setCodeFilter(BiFunction<String, String, Boolean> codeFilter) {
        this.codeFilter = codeFilter;
    }

    private static String mapCode(String authority, String code) {
        return authority + ":" + code;
    }

    private static boolean filterCode(String authority, String code) {
        // this pest shows up in every authority
        return !"WGS84(DD)".equals(code);
    }
}
